/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package isd.inventario.rn;



import isd.general.excepciones.ExcepcionGeneralSistema;
import isd.general.modelo.GR_Formulario;
import isd.general.rn.FormularioRN;
import isd.inventario.dao.InventarioDAO;
import isd.inventario.modelo.ST_Comprobante;
import isd.inventario.modelo.ST_ItemMovimiento;
import isd.inventario.modelo.ST_ItemProducto;
import isd.inventario.modelo.ST_ItemTransferencia;
import isd.inventario.modelo.ST_Movimiento;
import isd.inventario.modelo.ST_MovimientoPK;
import isd.inventario.modelo.ST_TipoItemMovimiento;
import isd.inventario.modelo.ST_TipoMovimiento;
import isd.inventario.modelo.ST_Stock;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;

/**
 *
 * @author Claudio
 */
@Stateless
@LocalBean
public class InventarioRN {
   
    @EJB private InventarioDAO inventarioDAO;
    @EJB private StockRN stockRN;   
    @EJB private FormularioRN formularioRN;

    /*
     * Se utiliza para generar movimientos desde el modulo de stock
     * este metodo incremenda el nro de formulario
     */
    public ST_Movimiento nuevoMovimiento(ST_Comprobante comprobante) throws ExcepcionGeneralSistema{

        if(comprobante==null) throw new ExcepcionGeneralSistema("El comprobante no puede ser nulo");
        if(comprobante.getTipoMovimiento()==null) throw new ExcepcionGeneralSistema("El comprobante no tiene definido el tipo de movimiento de inventario");

        //Buscamos el formulario correspondiente
        GR_Formulario formulario = formularioRN.getFormularioStock(comprobante);

        //incrementamos en 1 el numero de formulario
//        formulario.setUltimoNumero(formulario.getUltimoNumero() + 1);
        
        ST_Movimiento m = nuevoMovimiento(comprobante,formulario);
        return m;
    }

    /**
     * Se utiliza para generar movimientos de stock automáticos desde otros módulos
     * @param comprobante
     * @param formulario
     * @return
     * @throws ExcepcionGeneralSistema
     */

    public ST_Movimiento nuevoMovimientoAutomatico(ST_Comprobante comprobante, GR_Formulario formulario) throws ExcepcionGeneralSistema{

        return nuevoMovimiento(comprobante, formulario);
    }


    /**
     * Se utiliza para generar movimientos de stock automáticos
     * @param comprobante objeto comprobante a generar
     * @param formulario obejeto formulario a generar
     * @return movimiento de stock
     * @throws ExcepcionGeneralSistema
     */
    private ST_Movimiento nuevoMovimiento(ST_Comprobante comprobante, GR_Formulario formulario) throws ExcepcionGeneralSistema{
        
        ST_Movimiento m = new ST_Movimiento(formulario);

        m.setPersistido(false);
        m.setComprobante(comprobante);
        m.setFechaMovimiento(new Date());
        m.setSucursal(formulario.getSucursal());
        m.setTipoMovimiento(comprobante.getTipoMovimiento());

        if(comprobante.getDepositoIngreso()!=null && comprobante.getSectorIngreso()!=null){
            m.setDepositoIngreso(comprobante.getDepositoIngreso());
            m.setSectorIngreso(comprobante.getSectorIngreso());
        }

        if(comprobante.getDepositoEgreso()!=null && comprobante.getSectorEgreso()!=null){
            m.setDepositoEgreso(comprobante.getDepositoEgreso());
            m.setSectorEgreso(comprobante.getSectorEgreso());
        }
        
        //Generamos el item producto vacío
        m.setItemProducto(new ArrayList<ST_ItemProducto>());
        m.getItemProducto().add(nuevoItemProducto(m));        
        
        return m;
    }


    public ST_ItemProducto nuevoItemProducto(ST_Movimiento m){

        return (ST_ItemProducto) nuevoItemMovimiento(ST_TipoItemMovimiento.P, m );

    }

     public ST_ItemTransferencia nuevoItemTransferencia(ST_Movimiento m){

         return (ST_ItemTransferencia) nuevoItemMovimiento(ST_TipoItemMovimiento.T, m );

    }

    private ST_ItemMovimiento nuevoItemMovimiento(ST_TipoItemMovimiento ti,  ST_Movimiento m){

        ST_ItemMovimiento nItem = new ST_ItemProducto();

        if(ti.equals(ST_TipoItemMovimiento.P)){
            nItem = new ST_ItemProducto();
        }else{
            nItem = new ST_ItemTransferencia();
        }        

        nItem.setModfor(m.getModfor());
        nItem.setCodfor(m.getCodfor());
        nItem.setNrofor(m.getNrofor());
        nItem.setSucurs(m.getSucurs());        

        if(ti.equals(ST_TipoItemMovimiento.P)){
            nItem.setNroitm(m.getItemProducto().size()+1);
            nItem.setNroitd(m.getItemProducto().size()+1);
        }else{
            nItem.setNroitm(m.getItemTransferencia().size()+1);
            nItem.setNroitd(1001+m.getItemTransferencia().size());
        }
        
        nItem.setNivexp("1");
        nItem.setMovimiento(m);

        //Los depositos los asignamos antes de guardar el comprobante
        /**
        if(m.getDepositoEgreso()!=null){
            nItem.setDeposi(m.getDepositoEgreso().getDeposi());
        }

        if(m.getSectorEgreso()!=null){
           nItem.setSector(m.getSectorEgreso().getSector());
        }
         */

        if(m.getNserie()!=null){
            nItem.setNserie(m.getNserie());
        }else{
            nItem.setNserie("");
        }

        if(m.getNdespa()!=null){
            nItem.setNdespa(m.getNdespa());
        }else{
            nItem.setNdespa("");
        }

        if(m.getEnvase()!=null){
            nItem.setEnvase(m.getEnvase());
        }else{
            nItem.setEnvase("");
        }

        if(m.getNotros()!=null){
            nItem.setNotros(m.getNotros());
        }else{
            nItem.setNotros("");
        }

        if(m.getFechaMovimiento()!=null){
            SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
            nItem.setNfecha(sdf.format(m.getFechaMovimiento()));
        }else{
           nItem.setNfecha("");
        }

        nItem.setNatrib("");
        nItem.setNestan("");
        nItem.setNubica("");

        return nItem;
    }

    public synchronized  void guardar(ST_Movimiento m) throws Exception {

        limpiarItemsNoValidos(m);
        generarItemTransferencia(m);
        asignarDepositoItems(m);
        
        //Validamos que se pueda guardar le comprobante
        puedoGuardarMovimiento(m);       

        inventarioDAO.crear(m);
        generarStock(m);

        //Actualizamos el último número guardado de formulario
        formularioRN.actualizarUltimoNumero(m.getFormulario());
        
        m.setPersistido(true);
    }

    /**
     * Validaciones previas a guardar el movimiento
     * @param m Movimiento de stock
     */
    public void puedoGuardarMovimiento(ST_Movimiento m) throws ExcepcionGeneralSistema {

        if(getMovimiento(m.getModfor(),m.getCodfor(),m.getNrofor(),m.getSucurs())!=null){
            throw new ExcepcionGeneralSistema("El comprobante \""+m.getComprobante().getDescrp()+"\" con el número "+m.getNrofor()+" ya existe");
        }

        if(m.getItemProducto().isEmpty()){
            throw new ExcepcionGeneralSistema("El detalle de productos está vacío, no se puede guardar");
        }        

        //Si es transferencia el deposito de egreso tiene que estar cargado
        if(m.getTipoMovimiento().equals(ST_TipoMovimiento.T)){

            //Verificamos que el deposito ingreso siempre esté cargado
            if(m.getDepositoIngreso()==null){
                throw new ExcepcionGeneralSistema("El depósito no puede ser nulo");
            }else{
                if(m.getSectorIngreso()==null){
                    throw new ExcepcionGeneralSistema("El sector no puede ser nulo");
                }
            }

            if(m.getDepositoEgreso()==null){
                throw new ExcepcionGeneralSistema("El depósito para transferencia no puede ser nulo");
            }else{
                if(m.getSectorEgreso()==null){
                    throw new ExcepcionGeneralSistema("El sector para transferencia no puede ser nulo");
                }
            }
            
            if(m.getDepositoEgreso().equals(m.getDepositoIngreso())){
                throw new ExcepcionGeneralSistema("El depósito de egreso y de ingreso no pueden ser iguales");
            }
        }
        
        //Si es ingreso o ajuste el deposito de ingreso tiene que estar cargado
        if(m.getTipoMovimiento().equals(ST_TipoMovimiento.I) || m.getTipoMovimiento().equals(ST_TipoMovimiento.A)){
            
             if(m.getDepositoIngreso()==null){
                throw new ExcepcionGeneralSistema("El depósito no puede ser nulo");
            }else{
                if(m.getSectorIngreso()==null){
                    throw new ExcepcionGeneralSistema("El sector no puede ser nulo");        
                }
            }            
        }
        
        //Si es egreso el deposito de egreso tiene que estar cargado
        if(m.getTipoMovimiento().equals(ST_TipoMovimiento.E)){

            if(m.getDepositoEgreso()==null){
                throw new ExcepcionGeneralSistema("El depósito de egreso no puede ser nulo");
            }else{
                if(m.getSectorEgreso()==null){
                    throw new ExcepcionGeneralSistema("El sector de egreso no puede ser nulo");
                }
            }
        }

        verificarItemsProducto(m);
        
    }

    public void verificarItemsProducto(ST_Movimiento m) throws ExcepcionGeneralSistema{

        if(m.getTipoMovimiento().equals(ST_TipoMovimiento.E)){

            for(ST_ItemProducto i: m.getItemProducto()){

                if(i.getCantid()==null || i.getCantid().equals(BigDecimal.ZERO)){
                    
                    throw new ExcepcionGeneralSistema("Ingrese una valor válido para la cantidad en "+i.getProducto().getDescrp());
                }

                //Si el producto controla stock por numero de serie solicita obligatoriamente
                if(i.getProducto().getDefineSerie().equals("S")){
                    if(i.getNserie().isEmpty()){
                        throw new ExcepcionGeneralSistema("Ingrese serie para el producto "+ i.getProducto().getDescrp());
                    }
                }else{
                    i.setNserie("");
                }
            
                ST_Stock s = stockRN.nuevoStock(i);
                //Es un egreso de stock por lo tanto convertimos la cantidad a negativo
                s.setStocks(s.getStocks().negate());                
                if(!stockRN.isProductoDisponible(s)){
//                  System.out.println("En items producto");
                    throw new ExcepcionGeneralSistema("No existe stock suficiente para el producto "
                            + "" + i.getProducto().getDescrp() + " en el deposito " + i.getDeposi());
                }
            }
        }

        if(m.getItemTransferencia()!=null){

            for(ST_ItemTransferencia i: m.getItemTransferencia()){

                //Si el producto controla stock por numero de serie solicita obligatoriamente
                if(i.getProducto().getDefineSerie().equals("S")){
                    if(i.getNserie().isEmpty()){
                        throw new ExcepcionGeneralSistema("Ingrese serie para el producto "+ i.getProducto().getDescrp());
                    }
                }

                ST_Stock s = stockRN.nuevoStock(i);
                if(!stockRN.isProductoDisponible(s)){
                     throw new ExcepcionGeneralSistema("No existe stock suficiente para el producto "
                            + "" + i.getProducto().getDescrp() + " en el deposito " + i.getDeposi());
                }                
            }
        }
    }

    /**
     * Generamos los items de transferencia para registrar la salida del stock.
     * @param m Movimiento de stock
     */
    private void generarItemTransferencia(ST_Movimiento m) throws ExcepcionGeneralSistema{

        //Verificamos que se un movimiento de tipo transferencia
        if(m.getTipoMovimiento().equals(ST_TipoMovimiento.T)){
            //Generamos la lista vacía
            m.setItemTransferencia(new ArrayList<ST_ItemTransferencia>());           
        }else{
            return;
        }        

        if(m.getItemProducto()!=null){
            
            for(ST_ItemProducto i: m.getItemProducto()){

                if(i.getProducto()!=null){
                    ST_ItemTransferencia t = nuevoItemTransferencia(m);
                    t.setProducto(i.getProducto());
                    t.setProductoPRD(i.getProductoPRD());
                    t.setNserie(i.getNserie());
                    t.setEnvase(i.getEnvase());
                    t.setNdespa(i.getNdespa());
                    t.setNotros(i.getNotros());
                    t.setNfecha(i.getNfecha());
                    t.setNatrib(i.getNatrib());
                    t.setNubica(i.getNubica());
                    t.setNestan(i.getNestan());

                    if(i.getCantid()==null){
                        throw new ExcepcionGeneralSistema("Cantidad en blanco");
                    }

                    t.setCantid(i.getCantid().negate());
                    m.getItemTransferencia().add(t);

                }
            }
        }

    }


    /**
     * Generar los objetos stock donde se almacena el stock de los productos
     * por deposito, fecha, etc
     * @param m Movimiento de stock
     * @throws Exception
     */
    private synchronized void generarStock(ST_Movimiento m) throws Exception{

        for(ST_ItemProducto i: m.getItemProducto()){

            ST_Stock nStock = stockRN.nuevoStock(i);

            if(m.getTipoMovimiento().equals(ST_TipoMovimiento.E)){
                nStock.setStocks(nStock.getStocks().negate());
            }

            stockRN.guardar(nStock);
        }

        if(m.getItemTransferencia()!=null){

            for(ST_ItemTransferencia i: m.getItemTransferencia()){

                ST_Stock nStock = stockRN.nuevoStock(i);
                stockRN.guardar(nStock);
            }
        }
    }

    private void asignarDepositoItems(ST_Movimiento m) {

        if(m.getItemProducto()!=null){
            for(ST_ItemMovimiento i: m.getItemProducto()){

                if(m.getTipoMovimiento().equals(ST_TipoMovimiento.A)){
                    i.setDeposi(m.getDepositoIngreso().getDeposi());
                    i.setSector(m.getSectorIngreso().getSector());
                }

                if(m.getTipoMovimiento().equals(ST_TipoMovimiento.T)){

                    i.setDeposi(m.getDepositoIngreso().getDeposi());
                    i.setSector(m.getSectorIngreso().getSector());
                }

                if(m.getTipoMovimiento().equals(ST_TipoMovimiento.I)){
                    i.setDeposi(m.getDepositoIngreso().getDeposi());
                    i.setSector(m.getSectorIngreso().getSector());
                }

                if(m.getTipoMovimiento().equals(ST_TipoMovimiento.E)){
                    i.setDeposi(m.getDepositoEgreso().getDeposi());
                    i.setSector(m.getSectorEgreso().getSector());
                }
            }
        }
        
        //Aplicamos el deposito a los items de transferencia
        if(m.getTipoMovimiento().equals(ST_TipoMovimiento.T)){

            if(m.getItemTransferencia()!=null){
                for(ST_ItemMovimiento i: m.getItemTransferencia()){

                    i.setDeposi(m.getDepositoEgreso().getDeposi());
                    i.setSector(m.getSectorEgreso().getSector());
                }
            }
        }
    }

    public ST_Movimiento getMovimiento(String modfor,String codfor,Integer nrofor, String sucurs) {

        ST_MovimientoPK id = new ST_MovimientoPK(modfor, codfor, nrofor, sucurs);
        return inventarioDAO.getMovimiento(id);
    }

    /**
     * Borramos de la lista los items que no son válidos para guardar y que
     * pudieran generar errores
     * @param m Movimiento de ST_Stock
     */

    private void limpiarItemsNoValidos(ST_Movimiento m) {

        if (!m.getItemProducto().isEmpty()) {

            int indice = m.getItemProducto().size() - 1;
            ST_ItemProducto iaux = m.getItemProducto().get(indice);

            if(iaux.getProducto()==null){
                m.getItemProducto().remove(indice);
            }
        }
    }

    /**
     * Eliminar un item de un movimiento
     * @param mov movimiento del cual se eliminará el item
     * @param nItem item a eliminar
     */
    public boolean eliminarItemProducto(ST_Movimiento mov,  ST_ItemMovimiento nItem){

        boolean fItemBorrado = false;
        int i = 0;
        int indiceItemProducto = -1;
        
        for(ST_ItemProducto ip: mov.getItemProducto()){

            if(ip.getProducto()!=null){

                if(ip.getProducto().equals(nItem.getProducto())){
                    indiceItemProducto = i;
                }
            }
            i++;
        }

        i = 0;

        //Borramos los items productos
        if(indiceItemProducto>=0){
            ST_ItemProducto remove = mov.getItemProducto().remove(indiceItemProducto);
            if(remove!=null){
                fItemBorrado = true;
            }
        }


        return fItemBorrado;
    }   

}
